home *** CD-ROM | disk | FTP | other *** search
- /*
- ** Parse and execute dialing script. Ancient history says this was
- ** based on MMDF, but it wouldn't be recognized now...
- ** Copyright (c) 1991 Bolt Beranek and Newman, Inc.
- ** All rights reserved.
- **
- ** Redistribution and use in source and binary forms are permitted
- ** provided that: (1) source distributions retain this entire copyright
- ** notice and comment, and (2) distributions including binaries display
- ** the following acknowledgement: ``This product includes software
- ** developed by Bolt Beranek and Newman, Inc. and CREN/CSNET'' in the
- ** documentation or other materials provided with the distribution and in
- ** all advertising materials mentioning features or use of this software.
- ** Neither the name of Bolt Beranek and Newman nor CREN/CSNET may be used
- ** to endorse or promote products derived from this software without
- ** specific prior written permission.
- **
- ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
- ** WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
- ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
- /*
- * Copyright (c) 1992 Purdue University
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Purdue University. The name of the University may not be used
- * to endorse or promote products derived * from this software without
- * specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- * Note: this copyright applies to portions of this software developed
- * at Purdue beyond the software covered by the original copyright.
- */
- #include <stdio.h>
- #include <signal.h>
- #include <errno.h>
- #include <setjmp.h>
- #include <fcntl.h>
- #include <ctype.h>
- #include <errno.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <sys/ioctl.h>
- #include <sys/time.h>
- #include <sys/file.h>
- #include <sys/param.h>
- #include <sys/stream.h>
- #include <netinet/in.h>
- #include <netinet/in_systm.h>
- #include <netinet/ip.h>
- #include <sys/socket.h>
- #include <net/if.h>
- #include "dp_str.h"
- #include "dp.h"
- #include "dpd.h"
-
- /* Various constants. */
- #define MAXFILES 15
- #define MAXLINE 256
- #define MAXFIELDS 32 /* Maximum number of parameters */
- #define MAXRECVSTR MAXFIELDS - 2 /* Maximum number of recieve strings */
- #define MATCHLEN 128 /* Size of text to match in recv */
- #define XMITWAIT 60 /* How long to wait on a write */
- #define REPBUFSIZE 256 /* Size of replay buffer */
-
- #define SC_SLEEP 0377 /* Char for \x, sleep */
- #define SC_BREAK 0376 /* Char for \#, break */
-
- /* Command codes; must not overlap with D_xxx codes */
- #define S_ABORT 101 /* Abort the script */
- #define S_ALT 102 /* Alternate */
- #define S_COMMENT 103 /* Comment */
- #define S_GO 104 /* Start the protocol */
- #define S_LOG 105 /* Log a message */
- #define S_MARK 106 /* Mark the place to start a replay */
- #define S_RECV 107 /* Watch for a string */
- #define S_REPLAY 108 /* Reuse old input */
- #define S_SELEND 109 /* Select end */
- #define S_SELST 110 /* Select begin */
- #define S_USEFILE 111 /* Read script commands from another file */
- #define S_XMIT 112 /* Transmit a string */
- #define S_EOF 113 /* EOF in file */
-
- /* Error codes return by commands */
- #define D_CONTIN 2 /* Continue with process */
- #define D_OK 1 /* Everything is fine */
- #define D_NOMATCH 0 /* Input did not match */
- #define D_FATAL -1 /* Fatal error */
- #define D_NONFATAL -2 /* Possibly-recoverable error */
- #define D_INTRPT -3 /* Interrupt during system call */
-
- /* Error codes returned by parsing routines */
- #define ERR_NOWORD -1
- #define ERR_QUOTE -2
- #define ERR_FIELDS -3
- #define ERR_BADVAR -4
- #define ERR_TOOLONG -5
-
- /* Structure that keeps track of open files. */
- typedef struct _OPENFILE {
- char *scriptname;
- FILE *scriptfile;
- int linenumber;
- int nfields;
- char *fields[MAXFIELDS];
- } OPENFILE;
-
- /* Structure that defines a command */
- typedef struct _COMMAND {
- char *Name;
- int Code;
- int MinFields;
- int MaxFields;
- } COMMAND;
-
-
- static char scriptname[128]; /* Script filename */
- static char pushbuffer[MATCHLEN + 2]; /* Pushback buffer for matcher */
- static char *pushptr = pushbuffer; /* Pointer into buffer */
- static jmp_buf timerest;
- static int linenumber; /* Line number in script file */
- static int use_nfields; /* Number of "use" fields */
- static char *use_fields[MAXFIELDS]; /* Current "use" fields */
- static OPENFILE *openfiles[MAXFILES]; /* Stack of open files */
- static int nopenfiles; /* Number of open files */
- static FILE *scriptfile; /* Script file */
- static FILE *portfile; /* Modem file */
- static FILE *tranfile; /* Transcript file descriptor */
- static int transtyle; /* Transcript style */
- static int repcount; /* Chars left in replay */
- static int repcurr; /* Current replay char */
- static int repnext; /* Where to insert next char */
- static int repsize; /* Replay buffer size; constant */
- static int repfirst; /* First char in replay */
- static int replay; /* Doing a replay? */
- static int repbuff[REPBUFSIZE]; /* Replay buffer */
- static char WHERE[] = "dialscript"; /* Where we are, for logging */
- static COMMAND commands[] = {
- { "abort", S_ABORT, 0, 0 },
- { "alternate", S_ALT, 0, 0 },
- { "go", S_GO, 0, 0 },
- { "log", S_LOG, 1, 1 },
- { "mark", S_MARK, 0, 0 },
- { "recv", S_RECV, 2, 10 },
- { "replay", S_REPLAY, 0, 0 },
- { "use", S_USEFILE, 1, 10 },
- { "xmit", S_XMIT, 1, 1 },
- { "{", S_SELST, 0, 0 },
- { "}", S_SELEND, 0, 0 },
- { NULL }
- };
-
- extern char *strerror();
-
-
- /*
- ** Report a syntax error.
- */
- static void
- syntax_error(problem)
- char *problem;
- {
- d_log(DLOG_GENERAL, WHERE, "Syntax error in \"%s\", line %d:\n\t%s\n",
- scriptname, linenumber, problem);
- }
-
-
- /*
- ** Report an I/O error.
- */
- static void
- io_error(problem)
- char *problem;
- {
- d_log(DLOG_GENERAL, WHERE, "I/O error in \"%s\", line %d:\n\t%s\n",
- scriptname, linenumber, problem);
- }
-
-
- /*
- ** Report a miscellaneous error with errno.
- */
- static void
- misc_error(format, arg)
- char *format;
- char *arg;
- {
- char buff[256];
-
- (void)sprintf(buff, "Error in \"%s\", line %d:\n\t%s, %s",
- scriptname, linenumber, format, strerror(errno));
- d_log(DLOG_GENERAL, WHERE, buff, arg);
- }
-
-
- /*
- ** Return printable representation of char.
- */
- static char *
- seechar(c)
- char c;
- {
- static char buff[8];
-
- if (isprint(c) || c == '\n') {
- buff[0] = c;
- buff[1] = '\0';
- }
- else
- (void)sprintf(buff, "\\%03o", c);
- return buff;
- }
-
-
- /*
- ** Parse a C-style escape string. Returns the length of the string
- ** or -1 on error.
- */
- static int
- canonstring(in, out)
- register char *in;
- register char *out;
- {
- int count;
- char c;
- char *save;
-
- for (save = out; c = *in++; )
- if (c != '\\')
- *out++ = c;
- else {
- switch (c = *in++) {
- default: *out++ = c; break;
- case 'b': *out++ = '\b'; break;
- case 'n': *out++ = '\n'; break;
- case 'r': *out++ = '\r'; break;
- case 't': *out++ = '\t'; break;
- case 'x': *out++ = SC_SLEEP; break;
- case '#': *out++ = SC_BREAK; break;
- case '\0':
- return -1;
-
- case '0': case '1': case '2': case '3':
- case '4': case '5': case '6': case '7':
- for (c -= '0', count = 1; count < 3; count++, in++) {
- if (*in < '0' || *in > '7')
- break;
- c = (c << 3) | (*in - '0');
- }
- *out++ = c;
- }
- }
- *out = '\0';
- return out - save;
- }
-
-
- /*
- ** Snip the next work out of the buffer.
- */
- static int
- getword(p, start, next)
- register char *p;
- char **start;
- char **next;
- {
- /* Skip leading whitespace, see if there's anything left. */
- while (*p == ' ' || *p == '\t')
- p++;
- if (*p == '\0')
- return ERR_NOWORD;
-
- if (*p == '"') {
- for (*start = ++p; *p != '"'; p++)
- if (*p == '\0')
- return ERR_QUOTE;
- *p++ = '\0';
- }
- else {
- for (*start = p; *p && *p != ' ' && *p != '\t'; )
- p++;
- if (*p)
- *p++ = '\0';
- }
- *next = p;
- return D_OK;
- }
-
-
- #define END (&temp[MAXLINE - 2])
-
-
- /*
- ** Handle $-expansion
- */
- static int
- expanddollars(buff)
- char *buff;
- {
- char temp[MAXLINE];
- char *out;
- char *p;
- int i;
-
- for (out = temp, p = buff; *p; p++)
- if (*p != '$') {
- if (out >= END)
- return ERR_TOOLONG;
- *out++ = *p;
- }
- else if (*++p == '$') {
- if (out >= END)
- return ERR_TOOLONG;
- *out++ = *p;
- }
- else {
- if (!isdigit(*p) || (i = *p - '0') > use_nfields)
- return ERR_BADVAR;
- if (out + strlen(use_fields[i]) >= END)
- return ERR_TOOLONG;
- out += strlen(strcpy(out, use_fields[i]));
- }
-
- *out++ = '\0';
- (void)strcpy(buff, temp);
- return D_OK;
- }
-
-
- /*
- ** Parse a line and break it into fields. Return the number of fields
- ** or an error.
- */
- static int
- getfields(buff, maxfields, fields)
- char *buff;
- int maxfields;
- char *fields[];
- {
- char *p;
- char *next;
- int i;
-
- if (strchr(buff, '$') != NULL && (i = expanddollars(buff)) != D_OK)
- return i;
-
- for (i = 0, p = buff; i < maxfields; i++, p = next)
- switch (getword(p, &fields[i], &next)) {
- case ERR_NOWORD:
- return i;
- case ERR_QUOTE:
- return ERR_QUOTE;
- }
- return ERR_FIELDS;
- }
-
-
- /*
- ** Get a line of text and parse it into a command and fields. Return
- ** the command number or an error.
- */
- static int
- getcommand(buff, nfields, fields)
- char *buff;
- int *nfields;
- char *fields[];
- {
- char *p;
- register COMMAND *cp;
-
- do {
- linenumber++;
- if (fgets(buff, MAXLINE, scriptfile) == NULL)
- return S_EOF;
- if ((p = strchr(buff, '\n')) == NULL) {
- syntax_error("Line too long");
- return D_FATAL;
- }
- *p = '\0';
- } while (buff[0] == '\0' || buff[0] == '#');
-
- /* Get the first word. */
- switch (getword(buff, &fields[0], &p)) {
- default:
- return D_FATAL;
- case ERR_QUOTE:
- syntax_error("Missing end quote");
- return D_FATAL;
- case D_OK:
- break;
- }
-
- /* Find the command word. */
- for (cp = commands; cp->Name; cp++)
- if (strcmp(fields[0], cp->Name) == 0)
- break;
- if (cp->Name == NULL) {
- syntax_error("Bad command");
- return D_FATAL;
- }
-
- /* Parse the rest of the line. */
- *nfields = getfields(p, MAXFIELDS - 1, &fields[1]);
- if (*nfields < 0) {
- switch (*nfields) {
- default:
- syntax_error("Internal error in parser");
- break;
- case ERR_FIELDS:
- syntax_error("Too many fields");
- break;
- case ERR_QUOTE:
- syntax_error("Missing end quote");
- break;
- case ERR_BADVAR:
- syntax_error("Bad \"$\" variable");
- break;
- case ERR_TOOLONG:
- syntax_error("Line too long");
- break;
- }
- return D_FATAL;
- }
-
- /* Make sure the right number of fields are present. */
- if (*nfields >= cp->MinFields && *nfields <= cp->MaxFields)
- return cp->Code;
- syntax_error("Wrong number of fields");
- return D_FATAL;
- }
-
-
- /*
- ** Command dispatcher.
- */
- static int
- dispatch(c, nfields, fields)
- int c;
- int nfields;
- char *fields[];
- {
- int i;
-
- switch (c) {
- default:
- syntax_error("Unknown command in dispatch");
- return D_FATAL;
- case S_MARK:
- do_mark();
- break;
- case S_REPLAY:
- do_replay();
- break;
- case S_LOG:
- d_log(DLOG_GENERAL, WHERE, "%s", fields[1]);
- break;
- case S_COMMENT:
- break;
- case S_ABORT:
- return D_FATAL;
- case S_GO:
- return D_OK;
- case S_ALT:
- case S_SELST:
- case S_SELEND:
- return c;
- case S_XMIT:
- return (i = do_xmit(fields[1])) < 0 ? i : D_CONTIN;
- case S_RECV:
- return (i = do_recv(nfields, fields)) < 0 ? i : D_CONTIN;
- case S_USEFILE:
- return do_use(fields[1], nfields, fields) < 0 ? D_FATAL : D_CONTIN;
- case S_EOF:
- /* Unexpected EOF. Revert to previous script if possible. */
- return closescript() > 0 ? D_CONTIN : D_FATAL;
- }
- return D_CONTIN;
- }
-
-
- /*
- ** Execute all commands in a block.
- */
- static int
- do_block()
- {
- char buff[MAXLINE + 2];
- char *fields[MAXFIELDS];
- int nfields;
- int c;
- int i;
-
- for ( ; ; ) {
- if ((c = getcommand(buff, &nfields, fields)) < 0)
- return c;
- if ((i = dispatch(c, nfields, fields)) != D_CONTIN)
- return i;
- }
- }
-
-
- /*
- ** Skip forward until we hit the end of the current select block.
- */
- static int
- findblockend()
- {
- char buff[MAXLINE + 2];
- char *fields[MAXFIELDS];
- int nfields;
- int i;
-
- for ( ; ; ) {
- if ((i = getcommand(buff, &nfields, fields)) < 0)
- return i;
- switch (i) {
- case S_SELEND:
- return S_SELEND;
- case S_SELST:
- if ((i = findblockend()) < 0)
- return i;
- break;
- }
- }
- }
-
-
- /*
- ** Find next alternative in current select block.
- */
- static int
- findnextalt()
- {
- char buff[MAXLINE + 2];
- char *fields[MAXFIELDS];
- int nfields;
- int i;
-
- for ( ; ; ) {
- if ((i = getcommand(buff, &nfields, fields)) < 0)
- return i;
- switch (i) {
- case S_SELEND:
- case S_ALT:
- return i;
- case S_SELST:
- if ((findblockend()) < 0)
- return i;
- break;
- }
- }
- }
-
-
- static int
- runloop()
- {
- char buff[MAXLINE + 2];
- char *fields[MAXFIELDS];
- int nfields;
- int nselect;
- int i;
-
- /* Go for it. */
- for (nselect = 0; ; )
- switch (i = do_block()) {
- default:
- if (i < 0)
- return i;
- syntax_error("Internal error in runloop");
- return -1;
-
- case D_OK:
- return 0;
- case D_FATAL:
- return -1;
-
- case D_NONFATAL:
- /* Possibly-recoverable error. If in a select block, try the
- * next alternate. */
- if (nselect <= 0 || (i = findnextalt()) < 0)
- return -1;
- switch (i) {
- default:
- syntax_error("Bad syntax in script");
- return -1;
- case S_ALT:
- continue;
- case S_SELEND:
- return -1;
- }
-
- case S_SELST:
- /* The next line had better be an alternate */
- if (getcommand(buff, &nfields, fields) != S_ALT) {
- syntax_error("Missing \"alternate\" after \"{\"");
- return -1;
- }
- nselect++;
- continue;
-
- case S_SELEND:
- /* verify that there was a select block being looked at. If so,
- * then the last alternate was the successful one. Continue
- * normally. */
- if (nselect-- <= 0) {
- syntax_error("Bad \"}\"");
- return -1;
- }
- continue;
-
- case S_ALT:
- /* First, make sure the context was correct */
- if (nselect <= 0) {
- syntax_error("Bad alternate");
- return -1;
- }
- /* If we get here, then an alternate within a select was
- * completed successfully. Move on. */
- if ((i = findblockend()) < 0)
- return -1;
- nselect--;
- switch (i) {
- default:
- syntax_error("Syntax error in alternate");
- return -1;
- case S_SELEND:
- continue;
- }
- }
- }
-
- static char *
- copystring(p)
- char *p;
- {
- char *new;
-
- if ((new = malloc((unsigned int)strlen(p) + 1)) == NULL) {
- misc_error("Malloc failed; can't copy \"%s\"", p);
- return NULL;
- }
- return strcpy(new, p);
- }
-
- char *
- tr_dial(str, map)
- char *str;
- char *map;
- {
- char *s, *m;
- int nm, n;
- if (!map || !*map)
- return str;
-
- nm = strlen(map)/2;
- for (s = str ; *s ; s++)
- for (n = nm, m = map ; n-- ; m += 2)
- if (*s == *m) {
- *s = *(m+1);
- break;
- }
-
- return str;
- }
-
- /*
- ** Read lines from the script, parse them, do the actions.
- */
- int
- runscript(rp, fp, ms, tname, charmap)
- REMOTE *rp;
- FILE *fp;
- char *ms;
- char *tname;
- char *charmap;
- {
- int i;
- char *fields[MAXFIELDS];
- char **ap;
-
- /* Set up globals. */
- portfile = fp;
- if (tname == NULL)
- tranfile = NULL;
- else {
- tranfile = *tname == '+' ? fopen(++tname, "a") : fopen(tname, "w");
- if (tranfile == NULL) {
- misc_error("Can't create transcript \"%s\"", tname);
- return -1;
- }
- }
- transtyle = rp->Transtyle;
-
- /*
- * Set up the modem dialing script first..
- */
- fields[0] = use_fields[0] = copystring(ms);
- fields[1] = use_fields[1] = tr_dial(copystring(rp->Phone), charmap);
- i = do_use(ms, use_nfields = 2, fields) < 0 ? -1 : runloop();
- while (closescript())
- ;
- if (i < 0)
- i = -1;
-
- if (i >= 0) {
- /*
- * If call is successful, then do the login script.
- */
- fields[0] = use_fields[0] = copystring(rp->Script);
- use_nfields = 1;
- if (ap = rp->ScriptArgs)
- for ( ; *ap ; ap++, use_nfields++ )
- fields[use_nfields] = use_fields[use_nfields] = copystring(*ap);
-
- i = do_use(rp->Script, use_nfields, fields) < 0 ? -1 : runloop();
- while (closescript())
- ;
- if (i < 0)
- i = -2;
- }
-
- if (tranfile)
- (void)fclose(tranfile);
- return i;
- }
-
-
- /*
- ** Wait for a given string, with optional timeout.
- */
- static void
- scripttimeout()
- {
- longjmp(timerest, 1);
- }
-
-
- /*
- ** Write to the port, with a timeout.
- */
- static int
- writechar(p)
- char *p;
- {
- int i;
-
- if (tranfile && transtyle != TS_LOW) {
- if (isprint(*p))
- (void)fprintf(tranfile, "\tPUT %c (0%03o)\n", *p, *p);
- else
- (void)fprintf(tranfile, "\tPUT 0x%02x (0%03o)\n", *p, *p);
- (void)fflush(tranfile);
- }
-
- (void)signal(SIGALRM, scripttimeout);
- if (setjmp(timerest) == 0) {
- (void)alarm(XMITWAIT);
- i = write(fileno(portfile), p, 1);
- (void)alarm(0);
-
- /* Process the return codes. */
- if (i == 1)
- return D_OK;
- if (i >= 0)
- return D_NONFATAL;
- if (errno != EINTR)
- return D_FATAL;
- }
- io_error("Write time-out");
- return D_INTRPT;
- }
-
-
- /*
- ** Transmit a string.
- */
- do_xmit(string)
- char *string;
- {
- register int i;
- register char *p;
- char buff[MAXLINE];
-
- if (tranfile && transtyle != TS_LOW) {
- (void)fprintf(tranfile, "XMIT %s\n", string);
- (void)fflush(tranfile);
- }
-
- /* Make canonical. */
- if ((i = canonstring(string, buff)) < 0) {
- syntax_error("Bad transmit string format");
- return D_FATAL;
- }
-
- for (p = buff; --i >= 0; p++) {
- if ((unsigned char)*p == (unsigned char)SC_SLEEP) {
- if (tranfile && transtyle != TS_LOW) {
- (void)fprintf(tranfile, "\tSLEEP\n");
- (void)fflush(tranfile);
- }
- (void)sleep(1);
- }
- else if ((unsigned char)*p == (unsigned char)SC_BREAK) {
- if (tranfile && transtyle != TS_LOW) {
- (void)fprintf(tranfile, "\tBREAK\n");
- (void)fflush(tranfile);
- }
- (void)ioctl(fileno(portfile), TIOCSBRK, (caddr_t)0);
- (void)sleep(1);
- (void)ioctl(fileno(portfile), TIOCCBRK, (caddr_t)0);
- }
- else if (writechar(p) < 0)
- return D_FATAL;
- }
- return D_OK;
- }
-
-
- do_recv(nrecv_str, recv_str)
- int nrecv_str;
- char *recv_str[];
- {
- char *timestr = recv_str[nrecv_str];
- int length;
- int i;
- int err;
- int timeout;
-
- if (tranfile && transtyle != TS_LOW) {
- (void)fprintf(tranfile, "RECV ");
- for (i = 1 ; i <= nrecv_str ; i++)
- (void)fprintf(tranfile, "%s%c", recv_str[i],
- (i == nrecv_str) ? '\n' : ' ');
- (void)fflush(tranfile);
- }
-
- recv_str++;
- nrecv_str--;
- /* Convert the strings. */
- for (i = 0 ; i < nrecv_str ; i++) {
- if ((length = canonstring(recv_str[i], recv_str[i])) < 0) {
- syntax_error("Bad receive string format");
- return D_FATAL;
- }
- if (length > MATCHLEN) {
- syntax_error("Receive string too long");
- return D_FATAL;
- }
- }
-
- /* Set up the timer */
- if ((timeout = atoi(timestr)) < 0) {
- syntax_error("Bad timeout value");
- return D_FATAL;
- }
- if (timeout > 0)
- (void)signal(SIGALRM, scripttimeout);
-
- if (setjmp(timerest) == 0) {
- /* Do the matching. */
- if (timeout > 0)
- (void)alarm((unsigned int)timeout);
- for ( ; ; ) {
- for (i = 0 ; i < nrecv_str ; i++)
- if ((err = matchtext(recv_str[i], timeout)) == D_OK)
- break;
- if (i < nrecv_str)
- break;
- if (getnextchar(timeout) == D_CONTIN)
- longjmp(timerest, 2);
- }
- if (timeout > 0)
- (void)alarm(0);
-
- switch (err) {
- default:
- return D_FATAL;
- case D_OK:
- return D_OK;
- case D_NONFATAL:
- io_error("EOF while trying match");
- return D_NONFATAL;
- case D_FATAL:
- io_error("Read error while trying match");
- return D_FATAL;
- case D_INTRPT:
- break;
- }
- }
-
- d_log(DLOG_INFO, WHERE, "No match for \"%s\"%s after %d seconds",
- recv_str[0], nrecv_str > 1 ? " ..." : "", timeout);
- return D_NONFATAL;
- }
-
-
- #define pushbackchar(c) (*pushptr++ = (c))
-
-
- /*
- ** Check for a match between the string and the input stream.
- */
- int
- matchtext(p, timeout)
- char *p;
- int timeout;
- {
- int c;
- int i;
-
- if (*p == '\0')
- return D_OK;
- if ((c = getnextchar(timeout)) < D_OK)
- return c;
-
- if (c == D_CONTIN)
- return D_NOMATCH;
-
- if (c == *p) {
- i = matchtext(++p, timeout);
- if (i < D_OK)
- pushbackchar(c);
- return i;
- }
- pushbackchar(c);
- return D_NOMATCH;
- }
-
-
- /*
- ** Read characters from the port in raw mode, or the pushback buffer.
- */
- int
- getnextchar(timeout)
- int timeout;
- {
- int i;
- char c;
-
- /* Use pushback if there is any. */
- if (pushptr > pushbuffer) {
- c = *--pushptr;
- if (tranfile && transtyle != TS_LOW) {
- if (isprint(c))
- (void)fprintf(tranfile, "\t\tGET %4c (0%03o)\n", c, c);
- else
- (void)fprintf(tranfile, "\t\tGET 0x%02x (0%03o)\n", c, c);
- (void)fflush(tranfile);
- }
- return c;
- }
-
-
- while ((i = readchar(timeout)) != EOF) {
- /* filter out some of the junk characters */
- c = toascii(i);
- if (tranfile && transtyle != TS_LOW) {
- if (isprint(c))
- (void)fprintf(tranfile, "\t\tGET %4c (0%03o)\n", c, c);
- else
- (void)fprintf(tranfile, "\t\tGET 0x%02x (0%03o)\n", c, c);
- (void)fflush(tranfile);
- }
- if (c != '\0' && c != '\177')
- return c;
- /* Ignore NUL and DEL because they are almost always just noise */
- }
-
- if (i == EOF)
- return D_CONTIN; /* End of replay only... */
- if (feof(portfile))
- return D_NONFATAL;
- if (errno == EINTR)
- return D_INTRPT;
- return D_FATAL;
- }
-
-
- /*
- ** Open a script.
- */
- int
- do_use(sname, nfields, fields)
- char *sname;
- int nfields;
- char *fields[];
- {
- register int i;
- OPENFILE *op;
- FILE *F;
-
- if (nopenfiles > MAXFILES) {
- syntax_error("Too many \"use\" commands");
- return D_FATAL;
- }
-
- /* Get the full filename. */
- (void)strcpy(scriptname,
- expand_dirs_file("$DPCONF_DIR:$DPSCRIPT_DIR", sname));
-
- /* Open it. */
- if ((F = fopen(scriptname, "r")) == NULL) {
- misc_error("Can't open script file \"%s\"", scriptname);
- return D_FATAL;
- }
- if (tranfile && transtyle != TS_LOW) {
- (void)fprintf(tranfile, "USING %s", scriptname);
- for (i = 0; i < nfields; i++)
- (void)fprintf(tranfile, " %s", fields[i]);
- (void)fprintf(tranfile, "\n");
- (void)fflush(tranfile);
- }
-
- /* If this isn't the first file, save the current state. */
- if (nopenfiles) {
- if ((op = (OPENFILE *)malloc(sizeof (OPENFILE))) == NULL) {
- misc_error("No space for openfile \"%s\"", scriptname);
- return D_FATAL;
- }
- op->linenumber = linenumber;
- op->scriptfile = scriptfile;
- op->scriptname = copystring(scriptname);
- for (op->nfields = use_nfields, i = 0; i < use_nfields; i++)
- op->fields[i] = use_fields[i];
- for (use_nfields = nfields - 1, i = 0; i < nfields; i++)
- use_fields[i] = copystring(fields[i + 1]);
- openfiles[nopenfiles - 1] = op;
- }
- nopenfiles++;
-
- scriptfile = F;
- linenumber = 0;
- return D_OK;
- }
-
-
- int
- closescript()
- {
- int i;
- OPENFILE *op;
-
- if (nopenfiles <= 0)
- return 0;
-
- if (scriptfile)
- (void)fclose(scriptfile);
- for (i = 0; i < use_nfields; i++)
- free(use_fields[i]);
-
- if (--nopenfiles > 0) {
- op = openfiles[nopenfiles - 1];
- scriptfile = op->scriptfile;
- (void)strcpy(scriptname, op->scriptname);
- free(op->scriptname);
- linenumber = op->linenumber;
- for (use_nfields = op->nfields, i = 0; i < use_nfields; i++)
- use_fields[i] = op->fields[i];
- free((char *)op);
- return nopenfiles;
- }
- return 0;
- }
-
-
-
- /*
- ** Read characters from the TTY line and store them in a circular
- ** buffer so that they can be replayed if desired.
- */
-
- do_replay()
- {
- replay = 1;
- repcurr = repfirst;
- repcount = repsize;
- if (tranfile && transtyle != TS_LOW) {
- (void)fprintf(tranfile, "REPLAY\n");
- (void)fflush(tranfile);
- }
- }
-
-
- do_mark()
- {
- replay = 0;
- repsize = 0;
- repfirst = 0;
- repnext = 0;
- if (tranfile && transtyle != TS_LOW) {
- (void)fprintf(tranfile, "SET MARK\n");
- (void)fflush(tranfile);
- }
- }
-
-
- static int
- repskipline()
- {
- for (; repbuff[repfirst] != '\n' && repsize > 0; repsize--)
- repfirst++;
-
- if (repsize <= 0)
- do_mark();
- else {
- repfirst++;
- repsize--;
- }
- }
-
-
- int
- readchar(timeout)
- int timeout;
- {
- register int c;
-
- Top:
- if (replay) {
- /* Replaying input. Anything left? */
- if (repcount == 0) {
- replay = 0;
- goto Top;
- }
-
- c = repbuff[repcurr];
- if (++repcurr == REPBUFSIZE)
- repcurr = 0;
- repcount--;
-
- /* Don't replay EOF's. They get in there when a timeout interrupts
- * a read. */
- if (c == EOF)
- goto Top;
- }
- else {
- /*
- * Don't issue a read unless a timeout is active.
- * We could wait forever.
- */
- if (!timeout)
- return EOF;
- if (repsize == REPBUFSIZE)
- /* Ran out of buffer space. Rather then leave a partial line,
- * delete the first line in the buffer. */
- repskipline();
- else
- repsize++;
- repbuff[repnext] = c = getc(portfile);
- if (tranfile) {
- if (transtyle == TS_LOW)
- (void)fprintf(tranfile, "%s", seechar(c));
- else if (isprint(c))
- (void)fprintf(tranfile, "\tREAD %c (0%03o)\n", c, c);
- else
- (void)fprintf(tranfile, "\tREAD 0x%02x (0%03o)\n", c, c);
- (void)fflush(tranfile);
- }
- if (++repnext == REPBUFSIZE)
- repnext = 0;
- }
- return c;
- }
-